/*
<license>
This software is distributed under the Simplified BSD License:

Copyright (c) 2008, Chris Venter <chris.venter@gmail.com>
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, 
are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, 
    	this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice, 
    	this list of conditions and the following disclaimer in the documentation 
    	and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 
DAMAGE.
</license>
*/
#ifndef GENERIK_OS_UTILS_H
#define GENERIK_OS_UTILS_H

/** \file osutils.h
Some utilities which hide the os from the application.
*/

#include <stdlib.h>

#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>

#include <iostream>
#include <list>

#include <generik/tracing.h>

namespace generik
{

	/** Returns the current working directory as a std::string.
	*/
	inline std::string getcwd ()
	{
		char* buffer = ::getcwd(0, 0);
		std::string cwd = buffer;
		free(buffer);
		return cwd;
	}
	
	/** Returns the OS path seperator.
	As usual, this differs. On Unix it is "/", on Windows it is "\".
	Luckily we are saved here by Mingw32 on Windows, which does not seem to care very much.
	Which is why this function does not use any #ifdef magik.
	*/
	inline char pathseperator()
	{
		return '/';
	}
	
	/** Returns the OS extension seperator.
	Everyone seems happy to use ".", but better be safe and hide it in a 
	function!
	*/
	inline char extensionseperator()
	{
		return '.';
	}
	
	/** Processes all files and directories at a given location recursively.
	Applies the provided filefunc and dirfunc to each entry.
	*/
	template <typename FILEFUNC, typename DIRFUNC>
	void process_path(const std::string& location, FILEFUNC& filefunc, DIRFUNC& dirfunc)
	{
		std::string loc = location;
		if (loc[loc.length()-1] != pathseperator()) loc += pathseperator();
		
		// read the contents of the directory
		DIR* dir = opendir(loc.c_str());
		
		// for each entry (which is not a symlink)
		// if it is a file, run filefunc on it
		// if it is a directory, run dirfunc on it, and process it
		dirent* entry = readdir(dir);
		while (entry)
		{
			std::string name = entry->d_name;
			//std::cout<<"entry: "<<name<<std::endl;
			
			if (name == "." || name == "..")
			{
				//std::cout<<"skipping dot entry"<<std::endl;
			}
			else
			{
				std::string fullpath = loc + name;
				struct stat statbuf;
				stat(fullpath.c_str(), &statbuf);
				//if (!S_ISLNK(statbuf.st_mode))
				if (1)
				{
					if (S_ISDIR(statbuf.st_mode))
					{
						//std::cout<<"directory "<<fullpath<<std::endl;
						dirfunc(fullpath);
						process_path(fullpath, filefunc, dirfunc);
					}
					else
					if (S_ISREG(statbuf.st_mode))
					{
						//std::cout<<"regular file "<<fullpath<<std::endl;
						filefunc(fullpath);
					}
					else
					{
						//std::cout<<"unknown mode for file "<<fullpath<<std::endl;
					}
				}
				else
				{
					//std::cout<<"ignoring symlink: "<<fullpath<<std::endl;
				}
			}
			//
			entry = readdir(dir);
		}
		
	}
		
	/** This does splits a filename into its relevant parts.
	In principle it tries to do the same as perl's fileparts function.
	That is, it splits a given path into its directory, filename and extension
	components. It also has routines for creating a full path given these
	parts.
	
	\todo This is still a quick and dirty hack, and can use with some more
	intelligent splitting. Currently it looks for the last seperator in the path
	to determine where the directory ends and the filename begins.
	Since a filename can contain an escaped seperator character, this could
	be a problem!
	*/
	struct pathparts
	{
		std::string directory;
		std::string filename;
		std::string extension;
		
		pathparts(const std::string& path)
		{
			GENERIK_TRACER;
			
			std::size_t dirat = path.find_last_of(pathseperator());
			if (dirat != std::string::npos)
			{
				// if there is a "/" in the path, extract everything up to the last one into "directory"
				directory = path.substr(0, dirat);
			}
			GENERIK_TRACE_VALUE(directory);
			
			filename = path.substr(dirat+1);
			std::size_t extat = filename.find_last_of(".");
			
			extension = filename.substr(extat+1);
			filename = filename.substr(0, extat);
			GENERIK_TRACE_VALUE(filename);
			GENERIK_TRACE_VALUE(extension);
		}
		/** Concatenates a directory, filename and extension together into a full path.
		 \note This should be a plain function?
		 */
		static std::string fullpath(const std::string& d, const std::string& f, const std::string& e)
		{
			if (d.empty())
			{
				// if the directory is empty, do not output it.
				return f + extensionseperator() + e;
			}
			return d + pathseperator() + f + extensionseperator() + e;
		}
		std::string fullpath()
		{
			return fullpath(directory, filename, extension);
		}
		std::string fullfile()
		{
			return filename + extensionseperator() + extension;
		}
	};
	

}


#endif







